# 确保中文显示正常
import io
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# 标准库导入
import os
import re
import json
import logging
import requests
from datetime import datetime

# 第三方库导入
from flask import Flask, render_template, request, jsonify, send_from_directory
from werkzeug.utils import secure_filename
from bs4 import BeautifulSoup

# 自定义模块导入
from utils.douban_movie_utils import DoubanMovieUtils
from utils.ptskit_utils import PTSkitSpider
from utils.qbittorrent_utils import QBittorrentManager
from utils.transmission_utils import TransmissionManager
from utils.fnos_media_utils import FnosMediaFetcher
from utils.emby_media_utils import EmbyMediaUtils
from utils.tmdb_utils import TMDBUtils
from utils.local_resources_utils import LocalResourcesUtils
from utils.cinema_utils import CinemaUtils
from utils.madouqu_utils import MaDouQuUtils
from utils.javdb_utlis import JavDBUtils
from utils.user_management_utils import get_user_manager

# 创建本地资源管理工具实例
local_resources_utils = LocalResourcesUtils()

# 创建影院工具实例
cinema_utils = CinemaUtils()

# 创建Flask应用实例
app = Flask(__name__)

# 配置额外的静态文件目录
data_dir = os.path.join(os.path.dirname(__file__), 'data')
app.static_folder = 'static'

@app.route('/data/images/<path:filename>')
def serve_image(filename):
    """提供data/images目录中的图片访问"""
    return send_from_directory(os.path.join(data_dir, 'images'), filename)

import secrets
import string

# 配置应用
# 优先从环境变量获取SECRET_KEY，如果没有提供则生成随机密钥
if os.environ.get('SECRET_KEY'):
    app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
else:
    # 生成包含字母、数字和特殊字符的32位随机密钥
    alphabet = string.ascii_letters + string.digits + string.punctuation
    app.config['SECRET_KEY'] = ''.join(secrets.choice(alphabet) for _ in range(32))
    
app.config['DEBUG'] = os.environ.get('DEBUG', 'False').lower() == 'true'

# 权限控制装饰器
def permission_required(permission):
    """权限控制装饰器"""
    from functools import wraps
    from flask import session, redirect, url_for
    
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            # 检查用户是否登录
            if 'username' not in session:
                return redirect(url_for('login'))
            
            # 获取用户管理器
            user_manager = get_user_manager()
            
            # 检查用户是否存在
            # 这可以解决容器重新部署后cookie保留的问题
            if not user_manager.get_user(session['username']):
                # 用户不存在，清除会话
                session.clear()
                return redirect(url_for('login'))
            
            # 检查用户权限
            if not user_manager.check_permission(session['username'], permission):
                return "无权限访问此页面", 403
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator

# 用户登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
    """用户登录页面"""
    from flask import session, render_template, redirect, url_for, flash
    from utils.auth_utils import get_auth_manager
    
    auth_manager = get_auth_manager()
    
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        
        # 使用AuthManager处理登录
        success, message = auth_manager.login(username, password)
        
        if success:
            if message == "需要重置密码":
                # 用户需要重置密码，重定向到修改密码页面
                return redirect(url_for('change_password'))
            else:
                return redirect(url_for('index'))
        else:
            return render_template('login.html', error=message)
    
    return render_template('login.html')

# 用户注销路由
@app.route('/logout')
def logout():
    """用户注销"""
    from flask import redirect, url_for
    from utils.auth_utils import get_auth_manager
    
    # 使用AuthManager处理注销
    auth_manager = get_auth_manager()
    auth_manager.logout()
    
    return redirect(url_for('login'))

# 用户中心路由
@app.route('/user-center')
@permission_required('user_center')
def user_center():
    """用户中心页面"""
    from flask import session, render_template
    
    username = session['username']
    role = session['role']
    
    # 获取用户管理器
    user_manager = get_user_manager()
    
    # 获取当前用户信息
    current_user = user_manager.get_user(username)
    
    # 如果是管理员，获取所有用户信息
    all_users = []
    if role == 'admin':
        user_manager.load_users()
        all_users = user_manager.get_all_users()
    
    return render_template('user_center.html', current_user=current_user, all_users=all_users, role=role)

# 修改密码路由
@app.route('/change-password', methods=['GET', 'POST'])
@permission_required('user_center')
def change_password():
    """修改密码页面"""
    from flask import session, render_template, redirect, url_for
    from utils.auth_utils import get_auth_manager
    
    # 由于使用了permission_required装饰器，这里不需要再检查登录状态
    
    auth_manager = get_auth_manager()
    
    if request.method == 'POST':
        username = session['username']
        current_password = request.form.get('old_password')
        new_password = request.form.get('new_password')
        confirm_password = request.form.get('confirm_password')
        
        # 验证新密码和确认密码是否一致
        if new_password != confirm_password:
            return render_template('change_password.html', error='两次输入的新密码不一致')
        
        # 使用AuthManager修改密码
        success, message = auth_manager.change_password(username, current_password, new_password)
        
        if success:
            # 密码修改成功后需要重新登录
            return redirect(url_for('login', success='密码修改成功，请重新登录'))
        else:
            return render_template('change_password.html', error=message)
    
    return render_template('change_password.html')

# 创建用户API
@app.route('/api/users/create', methods=['POST'])
@permission_required('user_center')
def create_user():
    """创建新用户API"""
    from flask import session, jsonify
    
    # 只有管理员才能创建用户
    if session['role'] != 'admin':
        return jsonify({'success': False, 'message': '只有管理员才能创建用户'}), 403
    
    data = request.json
    username = data.get('username')
    password = data.get('password')
    role = data.get('role', 'user')
    
    # 获取用户管理器
    user_manager = get_user_manager()
    
    # 创建用户
    success, message = user_manager.create_user(username, password, role)
    
    if success:
        # 重新加载用户数据以确保单例实例中的数据是最新的
        user_manager.load_users()
        return jsonify({'success': True, 'message': message})
    else:
        return jsonify({'success': False, 'message': message}), 400

# 更新用户API
@app.route('/api/users/update/<username>', methods=['POST'])
@permission_required('user_center')
def update_user_api(username):
    """更新用户API"""
    from flask import session, jsonify
    
    # 只有管理员才能更新用户
    if session['role'] != 'admin':
        return jsonify({'success': False, 'message': '只有管理员才能更新用户'}), 403
    
    data = request.json
    new_password = data.get('password')
    new_role = data.get('role')
    new_avatar = data.get('avatar')
    
    # 获取用户管理器
    user_manager = get_user_manager()
    
    # 更新用户
    success, message = user_manager.update_user(username, new_password, new_role, new_avatar)
    
    if success:
        # 重新加载用户数据以确保单例实例中的数据是最新的
        user_manager.load_users()
        return jsonify({'success': True, 'message': message})
    else:
        return jsonify({'success': False, 'message': message}), 400

# 删除用户API
@app.route('/api/users/delete/<username>', methods=['POST'])
@permission_required('user_center')
def delete_user_api(username):
    """删除用户API"""
    from flask import session, jsonify
    
    # 只有管理员才能删除用户
    if session['role'] != 'admin':
        return jsonify({'success': False, 'message': '只有管理员才能删除用户'}), 403
    
    # 获取用户管理器
    user_manager = get_user_manager()
    
    # 删除用户
    success, message = user_manager.delete_user(username)
    
    if success:
        # 重新加载用户数据以确保单例实例中的数据是最新的
        user_manager.load_users()
        return jsonify({'success': True, 'message': message})
    else:
        return jsonify({'success': False, 'message': message}), 400

# 上下文处理器 - 添加用户信息到模板上下文
@app.context_processor
def inject_user_info():
    """将用户信息注入到模板上下文"""
    from flask import session
    from utils.user_management_utils import get_user_manager
    
    user_info = {
        'is_logged_in': 'username' in session,
        'username': session.get('username'),
        'role': session.get('role')
    }
    
    # 获取当前用户对象，包含详细信息
    current_user = None
    if 'username' in session:
        user_manager = get_user_manager()
        user_data = user_manager.get_user(session['username'])
        if user_data:
            current_user = {
                'username': user_data['username'],
                'role': user_data['role'],
                'is_admin': user_data['role'] == 'admin',
                'created_at': user_data.get('created_at'),
                'last_login': user_data.get('last_login'),
                'avatar': user_data.get('avatar', '/static/images/favction.png')
            }
    
    return {'user': user_info, 'current_user': current_user}

# 头像上传API
@app.route('/api/users/upload-avatar', methods=['POST'])
@permission_required('user_center')
def upload_avatar():
    """上传用户头像"""
    from flask import session, jsonify
    import os
    import uuid
    
    # 检查是否有文件上传
    if 'avatar' not in request.files:
        return jsonify({'success': False, 'message': '没有上传文件'}), 400
    
    file = request.files['avatar']
    
    # 检查文件名是否为空
    if file.filename == '':
        return jsonify({'success': False, 'message': '文件名不能为空'}), 400
    
    # 检查文件类型
    allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'}
    if '.' not in file.filename or file.filename.rsplit('.', 1)[1].lower() not in allowed_extensions:
        return jsonify({'success': False, 'message': '只支持PNG、JPG、JPEG、GIF格式的图片'}), 400
    
    # 获取用户名
    username = session['username']
    
    # 获取配置
    config = load_config()
    image_dir = config['app'].get('image_dir', './data/images')
    
    # 确保头像目录存在
    avatars_dir = os.path.join(image_dir, 'avatars')
    os.makedirs(avatars_dir, exist_ok=True)
    
    # 生成唯一的文件名
    unique_filename = f"{uuid.uuid4()}_{secure_filename(file.filename)}"
    
    # 保存文件
    file.save(os.path.join(avatars_dir, unique_filename))
    
    # 构建头像URL路径
    avatar_path = f"/data/images/avatars/{unique_filename}"
    
    # 更新用户头像
    user_manager = get_user_manager()
    success, message = user_manager.update_user(username, new_avatar=avatar_path)
    
    if success:
        # 重新加载用户数据
        user_manager.load_users()
        return jsonify({'success': True, 'message': '头像上传成功', 'avatar': avatar_path})
    else:
        return jsonify({'success': False, 'message': message}), 400

# 获取应用版本号
def get_app_version():
    """获取应用版本号"""
    return os.environ.get('APP_VERSION', 'dev')

# 加载配置文件
def load_config():
    """加载配置文件"""
    config_path = os.path.join(os.path.dirname(__file__), 'config', 'config.json')
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception as e:
        print(f"加载配置文件失败: {str(e)}")
        # 返回默认配置
        return {
            'douban': {'cookie': '', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'},
            'ptskit': {'cookie': ''},
            'qbittorrent': {
                'host': 'localhost',
                'port': 8080,
                'username': 'admin',
                'password': 'adminadmin'
            },
            'transmission': {
                'host': 'localhost',
                'port': 9091,
                'username': '',
                'password': ''
            },
            'fnos_media': {
                'server_ip': 'localhost',
                'server_port': 5666,
                'username': 'username',
                'password': 'password',
                'verify_ssl': False
            },
            'emby': {
                'server_url': 'localhost',
                'server_port': 8096,
                'api_key': '',
                'user_id': ''
            },
            'app': {
                'data_dir': './data',
                'nfo_dir': './data/nfo_files',
                'image_dir': './data/images'
            },
            'tmdb': {
                'api_key': '',
                'proxy_ip': 'localhost',
                'proxy_port': '7890'
            }
        }

# 确保数据目录存在
def ensure_directories():
    """确保数据目录存在"""
    config = load_config()
    app_dirs = ['data_dir', 'nfo_dir', 'image_dir']
    for dir_key in app_dirs:
        if dir_key in config['app']:
            dir_path = config['app'][dir_key]
            os.makedirs(dir_path, exist_ok=True)

# 初始化目录
ensure_directories()

# 上下文处理器 - 为所有模板提供app_version变量
@app.context_processor
def inject_app_version():
    return {'app_version': get_app_version()}

# 初始化 DoubanMovieUtils 引用（假设已正确导入）
from utils.douban_movie_utils import DoubanMovieUtils

# 初始化 qBittorrent 管理器
_qbittorrent_manager = None

def get_qbittorrent_manager():
    """获取qBittorrent管理器实例"""
    global _qbittorrent_manager
    if _qbittorrent_manager is None:
        config = load_config()
        qb_config = config.get('qbittorrent', {
            'host': 'localhost',
            'port': 8080,
            'username': 'admin',
            'password': 'adminadmin'
        })
        _qbittorrent_manager = QBittorrentManager(qb_config)
    return _qbittorrent_manager

# 初始化 Transmission 管理器
_transmission_manager = None

def get_transmission_manager():
    """获取Transmission管理器实例"""
    global _transmission_manager
    if _transmission_manager is None:
        config = load_config()
        tr_config = config.get('transmission', {
            'host': 'localhost',
            'port': 9091,
            'username': '',
            'password': ''
        })
        _transmission_manager = TransmissionManager(tr_config)
    return _transmission_manager

# 初始化飞牛媒体库管理器
_fnos_media_fetcher = None

# 初始化Emby媒体服务器管理器
_emby_media_utils = None

def get_fnos_media_fetcher():
    """获取飞牛媒体库管理器实例"""
    global _fnos_media_fetcher
    if _fnos_media_fetcher is None:
        config = load_config()
        # 确保fnos_media配置存在且正确读取
        fnos_config = config.get('fnos_media', {})
        # 设置默认值以避免配置缺失导致的错误
        server_ip = fnos_config.get('server_ip', 'localhost')
        server_port = fnos_config.get('server_port', 8088)
        username = fnos_config.get('username', 'admin')
        password = fnos_config.get('password', 'admin')
        verify_ssl = fnos_config.get('verify_ssl', False)
        
        app.logger.info(f"初始化飞牛媒体库连接: {server_ip}:{server_port}, 用户名: {username}")
        
        _fnos_media_fetcher = FnosMediaFetcher(
            server_ip=server_ip,
            server_port=server_port,
            verify_ssl=verify_ssl
        )
        
        success, message = _fnos_media_fetcher.login(username, password)
        if not success:
            app.logger.warning(f"飞牛媒体库登录失败: {message}")
            # 即使登录失败也返回实例，让上层代码处理错误
    return _fnos_media_fetcher

# 初始化豆瓣电影工具
def get_douban_movie_utils(keyword):
    """获取豆瓣电影工具实例"""
    config = load_config()
    cookie = config['douban'].get('cookie', '')
    image_dir = config['app'].get('image_dir', './data/images')
    return DoubanMovieUtils(cookie, keyword, poster_dir=image_dir)

# 初始化本地资源管理工具
def get_local_resources_utils():
    """获取本地资源管理工具实例"""
    return LocalResourcesUtils()

# 初始化TMDB工具
def get_tmdb_utils():
    """获取TMDB工具实例"""
    config = load_config()
    api_key = config.get('tmdb', {}).get('api_key', '')
    proxy_ip = config.get('tmdb', {}).get('proxy_ip', '')
    proxy_port = config.get('tmdb', {}).get('proxy_port', '')
    return TMDBUtils(api_key, proxy_ip, proxy_port)

# 初始化国产刮削工具
def get_madouqu_utils():
    """获取国产刮削工具实例"""
    return MaDouQuUtils()

# 初始化JavDB工具
def get_javdb_utils():
    """获取JavDB工具实例"""
    config = load_config()
    cookie = config.get('javdb', {}).get('cookie', '')
    return JavDBUtils(cookie)


# 本地影院页面路由
@app.route('/local-cinema')
@permission_required('local_cinema')
def local_cinema_page():
    """本地影院页面"""
    return render_template('local_cinema.html')


# API路由 - 获取视频列表
@app.route('/api/local-cinema/videos')
@permission_required('local_cinema')
def get_videos_list():
    """获取视频列表"""
    try:
        # 获取查询参数
        search_query = request.args.get('q', '')
        sort_option = request.args.get('sort', 'name_asc')
        path = request.args.get('path', '')
        
        # 调用工具类获取视频列表
        videos = cinema_utils.list_videos(directory=path or None, search_query=search_query)
        
        # 根据排序选项对结果进行排序
        if sort_option == 'name_asc':
            videos.sort(key=lambda x: x['name'].lower())
        elif sort_option == 'name_desc':
            videos.sort(key=lambda x: x['name'].lower(), reverse=True)
        elif sort_option == 'modified_asc':
            videos.sort(key=lambda x: x['last_modified'])
        elif sort_option == 'modified_desc':
            videos.sort(key=lambda x: x['last_modified'], reverse=True)
        elif sort_option == 'size_asc' and videos:
            # 先分离文件夹和文件，再对文件按大小排序
            folders = [v for v in videos if not v['is_file']]
            files = [v for v in videos if v['is_file']]
            files.sort(key=lambda x: x['size'] or 0)
            videos = folders + files
        elif sort_option == 'size_desc' and videos:
            # 先分离文件夹和文件，再对文件按大小排序
            folders = [v for v in videos if not v['is_file']]
            files = [v for v in videos if v['is_file']]
            files.sort(key=lambda x: x['size'] or 0, reverse=True)
            videos = folders + files
        
        return jsonify({
            'success': True,
            'data': videos
        }), 200
    except Exception as e:
        app.logger.error(f'获取视频列表错误: {str(e)}')
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500


# API路由 - 视频流式传输
@app.route('/api/local-cinema/stream')
def stream_cinema_video():
    """流式传输视频文件"""
    try:
        # 获取文件路径参数
        file_path = request.args.get('path', '')
        if not file_path:
            return jsonify({'success': False, 'message': '文件路径不能为空'}), 400
        
        # 解码URL编码的路径
        import urllib.parse
        decoded_path = urllib.parse.unquote(file_path)
        
        # 检查文件是否存在且是支持的视频格式
        is_valid, message = cinema_utils.validate_file_path(decoded_path)
        if not is_valid:
            return jsonify({'success': False, 'message': message}), 400
        
        # 获取文件大小
        file_size = os.path.getsize(decoded_path)
        
        # 获取请求头中的Range参数
        range_header = request.headers.get('Range', None)
        
        if range_header:
            # 处理部分请求（断点续传）
            byte_range = range_header.split('=')[1]
            start, end = byte_range.split('-')
            start = int(start)
            end = int(end) if end else file_size - 1
            
            # 确保范围有效
            length = end - start + 1
            
            # 打开文件并设置偏移量
            with open(decoded_path, 'rb') as f:
                f.seek(start)
                data = f.read(length)
            
            # 构建响应
            response = app.response_class(
                data,
                status=206,
                mimetype='video/' + os.path.splitext(decoded_path)[1][1:].lower(),
                direct_passthrough=True
            )
            
            # 设置响应头
            response.headers.add('Content-Range', f'bytes {start}-{end}/{file_size}')
            response.headers.add('Accept-Ranges', 'bytes')
            response.headers.add('Content-Length', length)
        else:
            # 处理完整请求
            response = app.response_class(
                open(decoded_path, 'rb'),
                mimetype='video/' + os.path.splitext(decoded_path)[1][1:].lower(),
                direct_passthrough=True
            )
            
            # 设置响应头
            response.headers.add('Content-Length', file_size)
            response.headers.add('Accept-Ranges', 'bytes')
        
        return response
    except Exception as e:
        app.logger.error(f'视频流式传输错误: {str(e)}')
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500


# API路由 - 获取视频信息
@app.route('/api/local-cinema/video-info')
def get_cinema_video_info():
    """获取视频详细信息"""
    try:
        # 获取文件路径参数
        file_path = request.args.get('path', '')
        if not file_path:
            return jsonify({'success': False, 'message': '文件路径不能为空'}), 400
        
        # 解码URL编码的路径
        import urllib.parse
        decoded_path = urllib.parse.unquote(file_path)
        
        # 获取视频信息
        video_info = cinema_utils.get_video_info(decoded_path)
        if not video_info or not video_info.get('success', False):
            return jsonify({
                'success': False,
                'message': video_info.get('message', '无法获取视频信息') if video_info else '无法获取视频信息'
            }), 500
        
        return jsonify({
            'success': True,
            'data': video_info['data']
        }), 200
    except Exception as e:
        app.logger.error(f'获取视频信息错误: {str(e)}')
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500
    proxy_port = config.get('tmdb', {}).get('proxy_port', '')
    
    # 为了向后兼容性，如果没有配置新的拆分参数但配置了旧的proxy参数，则使用旧参数
    if not proxy_ip and not proxy_port:
        proxy = config.get('tmdb', {}).get('proxy', '')
        return TMDBUtils(api_key, proxy=proxy)
    
    return TMDBUtils(api_key, proxy_ip=proxy_ip, proxy_port=proxy_port)

# 首页路由
@app.route('/')
@permission_required('index')
def index():
    """首页，显示系统状态和快速入口"""
    app_version = get_app_version()
    return render_template('index.html', app_version=app_version)

# 豆瓣刮削路由
@app.route('/douban')
@permission_required('admin')
def douban_scraper():
    """豆瓣刮削工具页面"""
    return render_template('douban.html')

# TMDB路由
@app.route('/tmdb')
@permission_required('admin')
def tmdb():
    """TMDB工具页面"""
    return render_template('tmdb.html')

# 国产刮削路由
@app.route('/madouqu')
@permission_required('admin')
def madouqu_scraper():
    """国产刮削工具页面"""
    return render_template('madouqu.html')

# 其他刮削路由
@app.route('/javdb')
@permission_required('admin')
def javdb_scraper():
    """其他刮削工具页面"""
    return render_template('javdb.html')

# API路由 - 搜索TMDB媒体
@app.route('/api/tmdb/search', methods=['GET'])
def search_tmdb_media():
    """搜索TMDB电影或电视节目API"""
    try:
        keyword = request.args.get('q', '')
        media_type = request.args.get('type', 'movie')  # movie或tv
        if not keyword:
            return jsonify({
                'success': False,
                'message': '请提供搜索关键词'
            }), 400
        
        tmdb_utils = get_tmdb_utils()
        success, result = tmdb_utils.search_media(keyword, media_type)
        
        if success:
            return jsonify({'success': True, 'data': result})
        else:
            return jsonify({'success': False, 'message': result}), 500
            
    except Exception as e:
        app.logger.error(f"搜索TMDB媒体失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'搜索失败: {str(e)}'
        }), 500

# API路由 - 搜索国产刮削内容
@app.route('/api/madouqu/search', methods=['GET'])
def search_madouqu():
    """搜索国产刮削内容API"""
    try:
        keyword = request.args.get('q', '')
        if not keyword:
            return jsonify({
                'success': False,
                'message': '请提供搜索关键词'
            }), 400
        
        # 由于madouqu不支持搜索，这里直接根据关键词构建结果
        # 这个实现仅为了满足前端接口需求
        results = [{
            'title': keyword,
            'info': '国产刮削',
            'actors': '未知',
            'code': keyword,
            'url': f'https://madouqu10.xyz/video/{keyword}'
        }]
        
        return jsonify({
            'success': True,
            'data': results
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 获取国产刮削详情
@app.route('/api/madouqu/detail', methods=['GET'])
def get_madouqu_detail():
    """获取国产刮削详情API"""
    try:
        url = request.args.get('url', '')
        if not url:
            return jsonify({
                'success': False,
                'message': '请提供详情URL'
            }), 400
            
        # 从URL中提取番号
        code = url.split('/')[-1]
        
        madouqu_utils = get_madouqu_utils()
        detail = madouqu_utils.extract_by_code(code)
        
        if detail:
            detail_data = {
                'title': detail.get('标题', '未知'),
                'original_title': detail.get('标题', ''),
                'originaltitle': detail.get('标题', ''),
                'actors': [detail.get('演员', '')] if detail.get('演员') else [],
                'url': url,
                'poster_url': detail.get('封面', ''),
                'code': code  # 添加code字段，确保与local_resources.js一致
            }
        else:
            detail_data = None
        
        return jsonify({
            'success': True,
            'data': detail_data
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 下载国产刮削海报
@app.route('/api/madouqu/download-poster', methods=['GET'])
def download_madouqu_poster():
    """下载国产刮削海报API"""
    try:
        poster_url = request.args.get('url', '')
        title = request.args.get('title', '')
        movie_id = request.args.get('id', '')
        
        if not poster_url:
            return jsonify({
                'success': False,
                'message': '请提供海报URL'
            }), 400
            
        # 复用豆瓣刮削的海报下载逻辑
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils('', title or "电影详情", poster_dir=image_dir)
        local_path = movie_utils.download_poster(poster_url, title or "电影海报", movie_id)
        
        if local_path:
            # 返回相对路径和文件名，以便前端可以访问和删除
            relative_path = os.path.relpath(local_path, os.path.dirname(__file__))
            filename = os.path.basename(local_path)
            return jsonify({
                'success': True,
                'local_path': relative_path.replace('\\', '/'),
                'filename': filename
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': '下载海报失败'
            }), 500
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 删除国产刮削临时海报文件
@app.route('/api/madouqu/delete-poster', methods=['POST'])
def delete_madouqu_poster():
    """删除国产刮削临时海报文件API"""
    try:
        data = request.json
        filename = data.get('filename', '')
        
        if not filename:
            return jsonify({
                'success': False,
                'message': '请提供文件名'
            }), 400
            
        # 复用豆瓣刮削的海报删除逻辑
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils('', "电影详情", poster_dir=image_dir)
        success = movie_utils.delete_poster(filename)
        
        if success:
            return jsonify({
                'success': True,
                'message': '海报已删除'
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': '删除海报失败'
            }), 500
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 生成国产刮削NFO文件
@app.route('/api/madouqu/generate-nfo', methods=['POST'])
def generate_madouqu_nfo():
    """生成国产刮削NFO文件API"""
    try:
        data = request.json
        url = data.get('url', '')
        
        if not url:
            return jsonify({
                'success': False,
                'message': '请提供详情URL'
            }), 400
            
        # 从URL中提取番号
        code = url.split('/')[-1]
        
        madouqu_utils = get_madouqu_utils()
        detail = madouqu_utils.extract_by_code(code)
        
        if not detail:
            return jsonify({
                'success': False,
                'message': '无法获取详情'
            }), 400
            
        # 生成NFO内容
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        cookie = config['douban'].get('cookie', '')
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils(cookie, detail.get('标题', 'movie'), poster_dir=image_dir)
        
        # 转换数据格式以适应NFO生成
        nfo_data = {
            'title': detail.get('标题', '未知'),
            'originaltitle': detail.get('标题', ''),
            'rating': '0',
            'rating_count': '0',
            'release_date': '1900-01-01',
            'genres': ['国产'],
            'directors': [],
            'writers': [],
            'actors': [detail.get('演员', '')] if detail.get('演员') else [],
            'introduction': '暂无简介',
            'runtime': '0',
            'poster_url': detail.get('封面', '')
        }
        
        nfo_content = movie_utils.generate_nfo(nfo_data)
        if not nfo_content:
            return jsonify({
                'success': False,
                'message': '生成NFO内容失败'
            }), 500
            
        # 获取配置中的NFO保存目录
        nfo_dir = config['app'].get('nfo_dir', './data/nfo_files')
        os.makedirs(nfo_dir, exist_ok=True)
        
        # 生成安全的文件名
        safe_title = re.sub(r'[\/:*?"<>|]', '', detail.get('标题', 'movie'))
        nfo_filename = f"{safe_title}.nfo"
        nfo_path = os.path.join(nfo_dir, nfo_filename)
        
        # 保存NFO文件
        with open(nfo_path, 'w', encoding='utf-8') as f:
            f.write(nfo_content)
            
        # 下载海报图片
        poster_path = None
        if detail.get('封面'):
            poster_path = movie_utils.download_poster(
                detail['封面'],
                detail.get('标题', 'movie'),
                code
            )
            
        return jsonify({
            'success': True,
            'nfo_content': nfo_content,
            'nfo_path': nfo_path,
            'poster_path': poster_path
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 获取24小时内最新电影
@app.route('/api/tmdb/recent-movies', methods=['GET'])
def get_recent_movies():
    """获取24小时内最新电影API"""
    try:
        tmdb_utils = get_tmdb_utils()
        success, result = tmdb_utils.get_recent_movies_24h()
        
        if success:
            return jsonify({'success': True, 'data': result})
        else:
            return jsonify({'success': False, 'message': result}), 500
            
    except Exception as e:
        app.logger.error(f"获取最新电影失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'获取失败: {str(e)}'
        }), 500

# API路由 - 搜索JavDB内容
@app.route('/api/javdb/search', methods=['GET'])
def search_javdb():
    """搜索JavDB内容API"""
    try:
        keyword = request.args.get('q', '')
        if not keyword:
            return jsonify({
                'success': False,
                'message': '请提供搜索关键词'
            }), 400
        
        javdb_utils = get_javdb_utils()
        result = javdb_utils.search_by_code(keyword)
        
        return jsonify({
            'success': True,
            'data': result
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 获取JavDB详情
@app.route('/api/javdb/detail', methods=['GET'])
def get_javdb_detail():
    """获取JavDB详情API"""
    try:
        url = request.args.get('url', '')
        if not url:
            return jsonify({
                'success': False,
                'message': '请提供详情URL'
            }), 400
            
        # 从URL中提取搜索关键词
        import urllib.parse
        parsed_url = urllib.parse.urlparse(url)
        query_params = urllib.parse.parse_qs(parsed_url.query)
        keyword = query_params.get('q', [''])[0]
        
        if not keyword:
            return jsonify({
                'success': False,
                'message': '无法从URL中提取搜索关键词'
            }), 400
            
        javdb_utils = get_javdb_utils()
        results = javdb_utils.search_by_code(keyword)
        
        # 取第一个结果，如果存在的话
        detail = results[0] if results else None
        
        if detail:
            detail_data = {
                'title': detail.get('标题', '未知'),
                'original_title': detail.get('标题', ''),
                'originaltitle': detail.get('标题', ''),
                'rating': '暂无评分',
                'year': detail.get('日期', '未知')[:4] if detail.get('日期') else '未知',
                'release_date': detail.get('日期', '未知'),
                'genres': detail.get('类别', '').split(',') if detail.get('类别') else [],
                'category': detail.get('类别', '').split(',') if detail.get('类别') else [],
                'duration': detail.get('时长', '未知'),
                'runtime': detail.get('时长', '未知'),
                'summary': f"番号: {detail.get('番号', '')}\n片商: {detail.get('片商', '')}",
                'introduction': f"番号: {detail.get('番号', '')}\n片商: {detail.get('片商', '')}",
                'directors': [detail.get('导演', '')] if detail.get('导演') else [],
                'writers': [],
                'actors': detail.get('演员', '').split(',') if detail.get('演员') else [],
                'url': url,
                'poster_url': detail.get('封面图片链接', ''),
                'code': detail.get('番号', ''),  # 添加code字段，确保与local_resources.js一致
                'studio': detail.get('片商', ''),  # 添加片商信息
                'publisher': detail.get('发行商', '')  # 添加发行商信息
            }
        else:
            detail_data = None
        
        return jsonify({
            'success': True,
            'data': detail_data
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 下载JavDB海报
@app.route('/api/javdb/download-poster', methods=['GET'])
def download_javdb_poster():
    """下载JavDB海报API"""
    try:
        poster_url = request.args.get('url', '')
        title = request.args.get('title', '')
        movie_id = request.args.get('id', '')
        
        if not poster_url:
            return jsonify({
                'success': False,
                'message': '请提供海报URL'
            }), 400
            
        # 复用豆瓣刮削的海报下载逻辑
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils('', title or "电影详情", poster_dir=image_dir)
        local_path = movie_utils.download_poster(poster_url, title or "电影海报", movie_id)
        
        if local_path:
            # 返回相对路径和文件名，以便前端可以访问和删除
            relative_path = os.path.relpath(local_path, os.path.dirname(__file__))
            filename = os.path.basename(local_path)
            return jsonify({
                'success': True,
                'local_path': relative_path.replace('\\', '/'),
                'filename': filename
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': '下载海报失败'
            }), 500
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 删除JavDB临时海报文件
@app.route('/api/javdb/delete-poster', methods=['POST'])
def delete_javdb_poster():
    """删除JavDB临时海报文件API"""
    try:
        data = request.json
        filename = data.get('filename', '')
        
        if not filename:
            return jsonify({
                'success': False,
                'message': '请提供文件名'
            }), 400
            
        # 复用豆瓣刮削的海报删除逻辑
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils('', "电影详情", poster_dir=image_dir)
        success = movie_utils.delete_poster(filename)
        
        if success:
            return jsonify({
                'success': True,
                'message': '海报已删除'
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': '删除海报失败'
            }), 500
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 生成JavDB NFO文件
@app.route('/api/javdb/generate-nfo', methods=['POST'])
def generate_javdb_nfo():
    """生成JavDB NFO文件API"""
    try:
        data = request.json
        # 接受code参数（前端发送的是code）
        code = data.get('code', '')
        
        if not code:
            return jsonify({
                'success': False,
                'message': '请提供番号'
            }), 400
            
        # 直接使用code作为搜索关键词
        keyword = code
            
        javdb_utils = get_javdb_utils()
        results = javdb_utils.search_by_code(keyword)
        
        # 取第一个结果，如果存在的话
        detail = results[0] if results else None
        
        if not detail:
            return jsonify({
                'success': False,
                'message': '无法获取详情'
            }), 400
            
        # 生成NFO内容
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        cookie = config['douban'].get('cookie', '')
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils(cookie, detail.get('标题', 'movie'), poster_dir=image_dir)
        
        # 转换数据格式以适应NFO生成
        nfo_data = {
            'title': detail.get('标题', '未知'),
            'originaltitle': detail.get('标题', ''),
            'rating': '0',
            'rating_count': '0',
            'release_date': detail.get('日期', '1900-01-01'),
            'genres': detail.get('类别', '').split(',') if detail.get('类别') else ['其他'],
            'directors': [detail.get('导演', '')] if detail.get('导演') else [],
            'writers': [],
            'actors': detail.get('演员', '').split(',') if detail.get('演员') else [],
            'introduction': f"番号: {detail.get('番号', '')}\n片商: {detail.get('片商', '')}",
            'runtime': detail.get('时长', '0').replace('分钟', '').strip() if detail.get('时长') else '0',
            'poster_url': detail.get('封面图片链接', '')
        }
        
        nfo_content = movie_utils.generate_nfo(nfo_data)
        if not nfo_content:
            return jsonify({
                'success': False,
                'message': '生成NFO内容失败'
            }), 500
            
        # 获取配置中的NFO保存目录
        nfo_dir = config['app'].get('nfo_dir', './data/nfo_files')
        os.makedirs(nfo_dir, exist_ok=True)
        
        # 生成安全的文件名
        safe_title = re.sub(r'[\/:*?"<>|]', '', detail.get('标题', 'movie'))
        nfo_filename = f"{safe_title}.nfo"
        nfo_path = os.path.join(nfo_dir, nfo_filename)
        
        # 保存NFO文件
        with open(nfo_path, 'w', encoding='utf-8') as f:
            f.write(nfo_content)
            
        # 下载海报图片
        poster_path = None
        if detail.get('封面图片链接'):
            poster_path = movie_utils.download_poster(
                detail['封面图片链接'],
                detail.get('标题', 'movie'),
                keyword
            )
            
        return jsonify({
            'success': True,
            'nfo_content': nfo_content,
            'nfo_path': nfo_path,
            'poster_path': poster_path
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 生成JavDB NFO文件 (GET方法，兼容前端JS调用)
@app.route('/api/javdb/generate_nfo', methods=['GET'])
def generate_javdb_nfo_get():
    """生成JavDB NFO文件API (GET方法)"""
    try:
        code = request.args.get('code', '')
        if not code:
            return jsonify({
                'success': False,
                'message': '请提供番号'
            }), 400
            
        javdb_utils = get_javdb_utils()
        detail = javdb_utils.search_by_code(code)
        
        if not detail:
            return jsonify({
                'success': False,
                'message': '无法获取详情'
            }), 400
            
        # 生成NFO内容
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        cookie = config['douban'].get('cookie', '')
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils(cookie, detail.get('标题', 'movie'), poster_dir=image_dir)
        
        # 转换数据格式以适应NFO生成
        nfo_data = {
            'title': detail.get('标题', '未知'),
            'originaltitle': detail.get('标题', ''),
            'rating': '0',
            'rating_count': '0',
            'release_date': detail.get('日期', '1900-01-01'),
            'genres': detail.get('类别', '').split(',') if detail.get('类别') else ['其他'],
            'directors': [detail.get('导演', '')] if detail.get('导演') else [],
            'writers': [],
            'actors': detail.get('演员', '').split(',') if detail.get('演员') else [],
            'introduction': f"番号: {detail.get('番号', '')}\n片商: {detail.get('片商', '')}",
            'runtime': detail.get('时长', '0').replace('分钟', '').strip() if detail.get('时长') else '0',
            'poster_url': detail.get('封面图片链接', '')
        }
        
        nfo_content = movie_utils.generate_nfo(nfo_data)
        if not nfo_content:
            return jsonify({
                'success': False,
                'message': '生成NFO内容失败'
            }), 500
            
        # 获取配置中的NFO保存目录
        nfo_dir = config['app'].get('nfo_dir', './data/nfo_files')
        os.makedirs(nfo_dir, exist_ok=True)
        
        # 生成安全的文件名
        safe_title = re.sub(r'[\/:*?"<>|]', '', detail.get('标题', 'movie'))
        nfo_filename = f"{safe_title}.nfo"
        nfo_path = os.path.join(nfo_dir, nfo_filename)
        
        # 保存NFO文件
        with open(nfo_path, 'w', encoding='utf-8') as f:
            f.write(nfo_content)
            
        # 下载海报图片
        poster_path = None
        if detail.get('封面图片链接'):
            poster_path = movie_utils.download_poster(
                detail['封面图片链接'],
                detail.get('标题', 'movie'),
                code
            )
            
        return jsonify({
            'success': True,
            'nfo_path': nfo_path
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 生成国产刮削NFO文件 (GET方法，兼容前端JS调用)
@app.route('/api/madouqu/generate_nfo', methods=['GET'])
def generate_madouqu_nfo_get():
    """生成国产刮削NFO文件API (GET方法)"""
    try:
        code = request.args.get('code', '')
        if not code:
            return jsonify({
                'success': False,
                'message': '请提供番号'
            }), 400
            
        madouqu_utils = get_madouqu_utils()
        detail = madouqu_utils.extract_by_code(code)
        
        if not detail:
            return jsonify({
                'success': False,
                'message': '无法获取详情'
            }), 400
            
        # 生成NFO内容
        from utils.douban_movie_utils import DoubanMovieUtils
        config = load_config()
        cookie = config['douban'].get('cookie', '')
        image_dir = config['app'].get('image_dir', './data/images')
        
        movie_utils = DoubanMovieUtils(cookie, detail.get('标题', 'movie'), poster_dir=image_dir)
        
        # 转换数据格式以适应NFO生成
        nfo_data = {
            'title': detail.get('标题', '未知'),
            'originaltitle': detail.get('标题', ''),
            'rating': '0',
            'rating_count': '0',
            'release_date': '1900-01-01',
            'genres': ['国产'],
            'directors': [],
            'writers': [],
            'actors': [detail.get('演员', '')] if detail.get('演员') else [],
            'introduction': '暂无简介',
            'runtime': '0',
            'poster_url': detail.get('封面', '')
        }
        
        nfo_content = movie_utils.generate_nfo(nfo_data)
        if not nfo_content:
            return jsonify({
                'success': False,
                'message': '生成NFO内容失败'
            }), 500
            
        # 获取配置中的NFO保存目录
        nfo_dir = config['app'].get('nfo_dir', './data/nfo_files')
        os.makedirs(nfo_dir, exist_ok=True)
        
        # 生成安全的文件名
        safe_title = re.sub(r'[\/:*?"<>|]', '', detail.get('标题', 'movie'))
        nfo_filename = f"{safe_title}.nfo"
        nfo_path = os.path.join(nfo_dir, nfo_filename)
        
        # 保存NFO文件
        with open(nfo_path, 'w', encoding='utf-8') as f:
            f.write(nfo_content)
            
        # 下载海报图片
        poster_path = None
        if detail.get('封面'):
            poster_path = movie_utils.download_poster(
                detail['封面'],
                detail.get('标题', 'movie'),
                code
            )
            
        return jsonify({
            'success': True,
            'nfo_path': nfo_path
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 下载NFO文件
@app.route('/api/download_nfo', methods=['GET'])
def download_nfo_file():
    """下载NFO文件API"""
    try:
        file_path = request.args.get('file_path', '')
        if not file_path:
            return jsonify({
                'success': False,
                'message': '请提供文件路径'
            }), 400
            
        # 确保路径安全，防止目录遍历攻击
        import os
        from flask import send_file
        
        # 获取配置中的NFO保存目录作为基础目录
        config = load_config()
        nfo_dir = config['app'].get('nfo_dir', './data/nfo_files')
        
        # 确保请求的文件在NFO目录内
        abs_file_path = os.path.abspath(file_path)
        abs_nfo_dir = os.path.abspath(nfo_dir)
        
        if not abs_file_path.startswith(abs_nfo_dir):
            return jsonify({
                'success': False,
                'message': '不允许访问该文件'
            }), 403
            
        if not os.path.exists(abs_file_path):
            return jsonify({
                'success': False,
                'message': '文件不存在'
            }), 404
            
        # 发送文件
        return send_file(abs_file_path, as_attachment=True)
        
    except Exception as e:
        app.logger.error(f"下载NFO文件失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'下载失败: {str(e)}'
        }), 500

# API路由 - 获取24小时内最新电视节目
@app.route('/api/tmdb/recent-tv', methods=['GET'])
def get_recent_tv():
    """获取24小时内最新电视节目API"""
    try:
        tmdb_utils = get_tmdb_utils()
        success, result = tmdb_utils.get_recent_tv_24h()
        
        if success:
            return jsonify({'success': True, 'data': result})
        else:
            return jsonify({'success': False, 'message': result}), 500
            
    except Exception as e:
        app.logger.error(f"获取最新电视节目失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'获取失败: {str(e)}'
        }), 500

# API路由 - 检查TMDB连接状态
@app.route('/api/tmdb/check-connectivity', methods=['GET'])
@permission_required('admin')
def check_tmdb_connectivity():
    """检查TMDB API连接状态"""
    try:
        tmdb_utils = get_tmdb_utils()
        success, message = tmdb_utils.check_connection()
        
        if success:
            app.logger.info("TMDB API连接成功")
            return jsonify({'success': True, 'message': '连接成功'})
        else:
            app.logger.warning(f"TMDB API连接失败: {message}")
            return jsonify({'success': False, 'message': message}), 500
            
    except Exception as e:
        app.logger.error(f"检查TMDB连接状态异常: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'连接失败: {str(e)}'
        }), 500

# API路由 - 获取TMDB媒体详情
@app.route('/api/tmdb/detail', methods=['GET'])
def get_tmdb_media_detail():
    """获取TMDB媒体详情API"""
    try:
        media_id = request.args.get('id', '')
        media_type = request.args.get('type', 'movie')  # movie或tv
        
        if not media_id:
            return jsonify({
                'success': False,
                'message': '请提供媒体ID'
            }), 400
            
        tmdb_utils = get_tmdb_utils()
        success, result = tmdb_utils.get_media_detail(media_id, media_type)
        
        if success:
            return jsonify({'success': True, 'data': result})
        else:
            return jsonify({'success': False, 'message': result}), 500
            
    except Exception as e:
        app.logger.error(f"获取TMDB媒体详情失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'获取失败: {str(e)}'
        }), 500

# API路由 - 下载TMDB媒体海报
@app.route('/api/tmdb/download-poster', methods=['GET'])
@permission_required('admin')
def download_tmdb_poster():
    """下载TMDB媒体海报API"""
    try:
        poster_url = request.args.get('url', '')
        media_id = request.args.get('id', '')
        title = request.args.get('title', '')
        
        if not poster_url:
            return jsonify({
                'success': False,
                'message': '请提供海报URL'
            }), 400
            
        # 使用工具类下载海报
        tmdb_utils = get_tmdb_utils()
        local_path = tmdb_utils.download_poster(poster_url, title or "媒体海报", media_id)
        
        if local_path and os.path.exists(local_path):
            # 直接返回海报文件
            directory = os.path.dirname(local_path)
            filename = os.path.basename(local_path)
            return send_from_directory(directory, filename, as_attachment=True)
        else:
            return jsonify({
                'success': False,
                'message': '下载海报失败'
            }), 500
    except Exception as e:
        app.logger.error(f'下载海报时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 生成TMDB媒体的NFO文件
@app.route('/api/tmdb/generate-nfo', methods=['GET', 'POST'])
@permission_required('admin')
def generate_tmdb_nfo():
    """生成TMDB媒体的NFO文件API"""
    try:
        # 同时支持GET和POST请求
        if request.method == 'GET':
            media_id = request.args.get('id', '')
            media_type = request.args.get('type', 'movie')
        else:
            data = request.json
            media_id = data.get('id', '')
            # 支持两种参数名，优先使用media_type
            media_type = data.get('media_type', data.get('type', 'movie'))
        
        if not media_id:
            return jsonify({
                'success': False,
                'message': '请提供媒体ID'
            }), 400
            
        tmdb_utils = get_tmdb_utils()
        # 先获取媒体详情
        success, detail = tmdb_utils.get_media_detail(media_id, media_type)
        
        if not success:
            app.logger.warning(f"获取媒体详情失败: {detail}")
            return jsonify({
                'success': False,
                'message': detail
            }), 404  # 使用404状态码表示资源未找到
            
        # 确保NFO文件存放在/app/data/nfo_files目录
        nfo_dir = '/app/data/nfo_files'
        os.makedirs(nfo_dir, exist_ok=True)
        
        # 生成安全的文件名
        media_title = detail.get('title', detail.get('name', 'media'))
        safe_title = re.sub(r'[\/:*?"<>|]', '', media_title)
        nfo_filename = f"{safe_title}.nfo"
        nfo_path = os.path.join(nfo_dir, nfo_filename)
        
        # 生成NFO文件
        try:
            # 直接设置环境变量，让utils知道使用哪个目录
            os.environ['TMDB_NFO_DIR'] = nfo_dir
            
            # 确保detail包含正确的类型信息
            if 'type' not in detail:
                detail['type'] = media_type
            
            # 尝试使用工具类生成NFO文件
            result = tmdb_utils.generate_nfo_file(detail)
            
            # 验证文件是否生成成功
            if not result or not os.path.exists(nfo_path):
                # 如果工具类生成失败或文件不存在，手动创建一个简单的NFO文件
                app.logger.info(f"手动创建NFO文件: {nfo_path}")
                
                # 根据媒体类型创建不同的XML结构
                root_tag = 'tvshow' if media_type == 'tv' else 'movie'
                title_tag = detail.get('title', detail.get('name', 'Unknown'))
                original_title_tag = detail.get('original_title', detail.get('original_name', title_tag))
                
                # 创建简单的XML内容
                nfo_content = f"<?xml version='1.0' encoding='UTF-8' standalone='yes'?><{root_tag}><title>{title_tag}</title><originaltitle>{original_title_tag}</originaltitle><overview>{detail.get('overview', '')}</overview><tmdbid>{media_id}</tmdbid></{root_tag}>"
                
                # 保存文件
                with open(nfo_path, 'w', encoding='utf-8') as f:
                    f.write(nfo_content)
                
            app.logger.info(f"NFO文件生成成功: {nfo_path}")
        except Exception as e:
            app.logger.error(f"生成NFO文件时发生错误: {str(e)}", exc_info=True)
            return jsonify({
                'success': False,
                'message': f'生成NFO文件失败: {str(e)}'
            }), 500
        
        if not os.path.exists(nfo_path):
            return jsonify({
                'success': False,
                'message': '生成NFO文件失败'
            }), 500
            
        # 下载海报图片
        poster_path = None
        if detail.get('poster_url'):
            poster_path = tmdb_utils.download_poster(
                detail['poster_url'],
                media_title,
                media_id
            )
        
        return jsonify({
            'success': True,
            'data': {
                'nfo_path': nfo_path,
                'poster_path': poster_path,
                'media_info': detail
            }
        }), 200
        
    except Exception as e:
        app.logger.error(f"生成NFO文件失败: {str(e)}", exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 搜索豆瓣电影
@app.route('/api/douban/search', methods=['GET'])
def search_douban_movie():
    """搜索豆瓣电影API"""
    try:
        keyword = request.args.get('q', '')
        if not keyword:
            return jsonify({
                'success': False,
                'message': '请提供搜索关键词'
            }), 400
            
        movie_utils = get_douban_movie_utils(keyword)
        results = movie_utils.search()
        
        # 格式化搜索结果，确保与前端期望的格式一致
        formatted_results = []
        if results:
            for item in results:
                formatted_results.append({
                    'title': item.get('title', '未知'),
                    'info': f"{item.get('type', '未知')} / {item.get('year', '未知')}",
                    'rating': item.get('rating', '暂无评分'),
                    'year': item.get('year', '未知'),
                    'url': item.get('url', '')
                })
        
        return jsonify({
            'success': True,
            'data': formatted_results
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# 获取Emby媒体服务器管理器
def get_emby_media_utils():
    """获取Emby媒体服务器管理器实例"""
    global _emby_media_utils
    if _emby_media_utils is None:
        config = load_config()
        emby_config = config.get('emby', {})
        server_url = emby_config.get('server_url', 'localhost')
        server_port = emby_config.get('server_port', 8096)
        api_key = emby_config.get('api_key', '')
        user_id = emby_config.get('user_id', '')
        
        app.logger.info(f"初始化Emby媒体服务器连接: {server_url}:{server_port}")
        
        _emby_media_utils = EmbyMediaUtils(
            server_url=server_url,
            server_port=server_port,
            api_key=api_key,
            user_id=user_id
        )
    return _emby_media_utils

# 配置日志
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('douban_api')

# API路由 - 获取豆瓣 电影详情
@app.route('/api/douban/detail', methods=['GET'])
def get_douban_movie_detail():
    """获取豆瓣电影详情API"""
    try:
        url = request.args.get('url', '')
        logger.debug(f'接收到电影详情请求，URL: {url}')
        
        if not url:
            logger.warning('未提供电影详情URL')
            return jsonify({
                'success': False,
                'message': '请提供电影详情URL'
            }), 400
            
        # 从URL中提取电影名称作为keyword参数
        movie_utils = get_douban_movie_utils("电影详情")  # 使用通用名称初始化
        detail = movie_utils.get_detail_info(url)
        
        logger.debug(f'从工具类获取的原始详情数据: {detail}')
        
        # 确保返回的数据包含前端期望的字段
        if detail:
            detail_data = {
                'title': detail.get('title', '未知'),
                'original_title': detail.get('originaltitle', ''),
                'originaltitle': detail.get('originaltitle', ''),  # 保留原始字段名
                'rating': detail.get('rating', '暂无评分'),
                'rating_count': detail.get('rating_count', 0),
                'year': detail.get('release_date', '未知')[:4] if detail.get('release_date') else '未知',
                'release_date': detail.get('release_date', '未知'),  # 保留原始字段名
                'genres': detail.get('genres', []),
                'category': detail.get('category', []),  # 保留原始字段名
                'duration': detail.get('runtime', '未知'),
                'runtime': detail.get('runtime', '未知'),  # 保留原始字段名
                'summary': detail.get('introduction', '暂无简介'),
                'introduction': detail.get('introduction', '暂无简介'),  # 保留原始字段名
                'directors': detail.get('directors', []),
                'writers': detail.get('writers', []),
                'actors': detail.get('actors', []),
                'url': url,
                'poster_url': detail.get('poster_url', ''),
                'info': detail.get('info', '')  # 添加原始info字段
            }
            logger.debug(f'处理后返回给前端的数据: {detail_data}')
        else:
            logger.warning('未获取到电影详情数据')
            detail_data = None
        
        return jsonify({
            'success': True,
            'data': detail_data
        }), 200
        
    except Exception as e:
        logger.error(f'获取电影详情时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 下载电影海报
@app.route('/api/douban/download-poster', methods=['GET'])
def download_poster():
    """下载    下载电影海报API
    修复了原代码中缺少except块的语法错误
    """
    try:
        poster_url = request.args.get('url', '')
        movie_id = request.args.get('id', '')
        title = request.args.get('title', '')
        
        if not poster_url:
            return jsonify({
                'success': False,
                'message': '请提供海报URL'
            }), 400
            
        # 使用工具类下载海报
        movie_utils = get_douban_movie_utils(title or "电影详情")
        local_path = movie_utils.download_poster(poster_url, title or "电影海报", movie_id)
        
        if local_path:
            # 返回相对路径和文件名，以便前端可以访问和删除
            relative_path = os.path.relpath(local_path, os.path.dirname(__file__))
            filename = os.path.basename(local_path)
            return jsonify({
                'success': True,
                'local_path': relative_path.replace('\\', '/'),
                'filename': filename
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': '下载海报失败'
            }), 500
    except Exception as e:
        # 添加了缺失的except块，修复了语法错误
        logger.error(f'下载海报时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 读取本地资源
@app.route('/api/local-resources/list', methods=['GET'])
@permission_required('admin')
def list_local_resources():
    """读取本地资源列表API"""
    try:
        # 获取查询参数
        resource_type = request.args.get('type', 'all')  # all, file, folder
        search_query = request.args.get('q', '')
        sort_option = request.args.get('sort', 'name-asc')
        path = request.args.get('path', '')
        recursive = request.args.get('recursive', 'true').lower() == 'true'
        
        # 使用本地资源管理工具获取资源列表
        # 如果提供了path参数，先保存原来的默认目录，然后临时修改
        original_default_dir = local_resources_utils.DEFAULT_RESOURCE_DIR
        if path and path.strip() != '':
            local_resources_utils.DEFAULT_RESOURCE_DIR = path
        
        success, result = local_resources_utils.list_resources(resource_type, search_query, sort_option, recursive)
        
        # 恢复原始默认目录
        local_resources_utils.DEFAULT_RESOURCE_DIR = original_default_dir
        
        if success:
            return jsonify({
                'success': True,
                'data': result
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': result
            }), 500
        
    except Exception as e:
        logger.error(f'读取本地资源时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 删除临时海报文件
@app.route('/api/douban/delete-poster', methods=['POST'])
def delete_poster():
    """删除临时海报文件API"""
    try:
        data = request.json
        filename = data.get('filename', '')
        
        if not filename:
            return jsonify({
                'success': False,
                'message': '请提供文件名'
            }), 400
            
        # 获取配置中的图片保存目录
        config = load_config()
        image_dir = config['app'].get('image_dir', './data/images')
        filepath = os.path.join(image_dir, filename)
        
        # 检查文件是否存在并删除
        if os.path.exists(filepath):
            os.remove(filepath)
            logger.debug(f'临时海报文件已删除: {filepath}')
            return jsonify({
                'success': True,
                'message': '文件已删除'
            }), 200
        else:
            logger.warning(f'文件不存在: {filepath}')
            return jsonify({
                'success': True,
                'message': '文件不存在'
            }), 200
    except Exception as e:
        logger.error(f'删除海报文件时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 获取Emby媒体库列表
@app.route('/api/emby_media/library-list', methods=['GET'])
def get_emby_library_list():
    """获取Emby媒体库列表API"""
    try:
        emby_utils = get_emby_media_utils()
        result = emby_utils.get_media_libraries()
        
        if result and 'libraries' in result:
            app.logger.info(f"成功获取Emby媒体库列表，共{len(result['libraries'])}个媒体库")
            return jsonify({'success': True, 'data': result['libraries']})
        else:
            app.logger.warning("未找到Emby媒体库")
            return jsonify({'success': True, 'data': []})
            
    except Exception as e:
        app.logger.error(f"获取Emby媒体库列表异常: {str(e)}")
        return jsonify({'success': False, 'message': f'获取失败: {str(e)}'}), 500

# API路由 - 获取Emby媒体库内容
@app.route('/api/emby_media/library-content', methods=['GET'])
def get_emby_library_content():
    """获取Emby媒体库内容API"""
    try:
        library_id = request.args.get('library_id')
        if not library_id:
            return jsonify({'success': False, 'message': '请提供媒体库ID'}), 400
        
        emby_utils = get_emby_media_utils()
        result = emby_utils.get_library_resources(library_id)
        
        if result and 'resources' in result:
            app.logger.info(f"成功获取媒体库{library_id}中的媒体条目，共{len(result['resources'])}个条目")
            return jsonify({'success': True, 'data': result['resources']})
        else:
            app.logger.warning(f"未找到媒体库{library_id}中的媒体条目")
            return jsonify({'success': True, 'data': []})
            
    except Exception as e:
        app.logger.error(f"获取Emby媒体库内容异常: {str(e)}")
        return jsonify({'success': False, 'message': f'获取失败: {str(e)}'}), 500

# API路由 - 检查Emby连接状态
@app.route('/api/emby_media/check-connectivity', methods=['GET'])
@permission_required('admin')
def check_emby_connectivity():
    """检查Emby服务器连接状态API"""
    try:
        emby_utils = get_emby_media_utils()
        # 尝试获取媒体库列表来检查连接性
        result = emby_utils.get_media_libraries()
        
        if result:
            app.logger.info("Emby服务器连接成功")
            return jsonify({'success': True, 'message': '连接成功'})
        else:
            app.logger.warning("Emby服务器连接失败")
            return jsonify({'success': False, 'message': '连接失败'}), 500
            
    except Exception as e:
        app.logger.error(f"检查Emby连接状态异常: {str(e)}")
        return jsonify({'success': False, 'message': f'连接失败: {str(e)}'}), 500

# API路由 - 检查本地资源挂载状态
@app.route('/api/local-resources/check-mount', methods=['GET'])
@permission_required('admin')
def check_mount():
    """检查本地资源挂载状态API"""
    try:
        # 使用本地资源管理工具检查挂载状态
        mount_status = local_resources_utils.check_mount_status()
        
        # 检查是否有错误
        if 'error' in mount_status:
            return jsonify({
                'success': False,
                'message': mount_status['error']
            }), 500
        
        return jsonify({
            'success': True,
            'data': {
                'is_mounted': mount_status['is_mounted'],
                'mount_path': mount_status['mount_path']
            }
        }), 200
        
    except Exception as e:
        logger.error(f'检查本地资源挂载状态时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 获取海报图片
@app.route('/api/local-resources/get-poster', methods=['GET'])
def get_poster():
    """获取本地资源目录中的海报图片API"""
    try:
        # 获取请求中的海报路径
        poster_path = request.args.get('path', '')
        
        if not poster_path:
            return jsonify({
                'success': False,
                'message': '请提供海报路径'
            }), 400
        
        # 确保路径安全，防止路径遍历攻击
        if '..' in poster_path:
            return jsonify({
                'success': False,
                'message': '路径不合法'
            }), 400
        
        # 检查文件是否存在并且是jpg文件
        if not os.path.exists(poster_path) or not poster_path.lower().endswith('.jpg'):
            return jsonify({
                'success': False,
                'message': '海报文件不存在'
            }), 404
        
        # 获取文件的目录和文件名
        file_dir = os.path.dirname(poster_path)
        file_name = os.path.basename(poster_path)
        
        # 提供文件下载
        return send_from_directory(file_dir, file_name)
        
    except Exception as e:
        logger.error(f'获取海报图片时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 保存刮削数据到资源目录
@app.route('/api/local-resources/save-scraped-data', methods=['POST'])
def save_scraped_data():
    """保存刮削数据到资源目录API"""
    try:
        data = request.json
        resource_path = data.get('resource_path', '')
        movie_info = data.get('movie_info', {})
        poster_path = data.get('poster_path', '')
        
        # 如果没有NFO内容，使用电影信息生成
        if 'nfo_content' not in movie_info or not movie_info['nfo_content']:
            # 根据数据源类型选择正确的工具来生成NFO内容
            if 'tmdb_id' in movie_info or ('media_type' in movie_info and movie_info['media_type'] in ['movie', 'tv']):
                # TMDB数据源
                # 由于TMDB已经在generate-nfo接口中生成了NFO文件，这里我们不需要再生成
                # 但为了兼容，可以创建一个简单的NFO内容
                media_type = movie_info.get('media_type', 'movie')
                root_tag = 'tvshow' if media_type == 'tv' else 'movie'
                title = movie_info.get('title', movie_info.get('name', 'Unknown'))
                original_title = movie_info.get('original_title', movie_info.get('original_name', title))
                overview = movie_info.get('overview', '')
                
                # 创建简单的XML内容
                movie_info['nfo_content'] = f"<?xml version='1.0' encoding='UTF-8' standalone='yes'?><{root_tag}><title>{title}</title><originaltitle>{original_title}</originaltitle><overview>{overview}</overview><tmdbid>{movie_info.get('id', '')}</tmdbid></{root_tag}>"
            else:
                # 豆瓣数据源
                from utils.douban_movie_utils import DoubanMovieUtils
                
                # 加载配置获取cookie
                config = load_config()
                cookie = config['douban'].get('cookie', '')
                
                # 从电影信息中提取标题作为媒体名称
                media_name = movie_info.get('title', '电影详情')
                
                # 获取图片保存目录
                image_dir = config['app'].get('image_dir', './data/images')
                
                # 正确初始化DoubanMovieUtils
                movie_utils = DoubanMovieUtils(cookie, media_name, poster_dir=image_dir)
                movie_info['nfo_content'] = movie_utils.generate_nfo(movie_info)
                
                # 增强的标题处理逻辑，确保movie_info包含正确的title字段
                try:
                    # 记录初始标题信息用于调试
                    logger.info(f"处理前的movie_info标题: {movie_info.get('title')}")
                    logger.info(f"movie_info完整结构: {movie_info}")
                    
                    # 尝试从各个可能的来源获取标题
                    title_sources = []
                    
                    # 来源1: 直接从movie_info中的title（最优先，因为前端已经处理过）
                    if movie_info.get('title') and movie_info['title'] and movie_info['title'].strip() and movie_info['title'].strip() not in ['未知视频', '未知', '', '电影详情', 'movie']:
                        title_sources.append(('movie_info.title', movie_info['title']))
                    
                    # 来源2: movie_info中的original_title
                    if movie_info.get('original_title') and movie_info['original_title'] and movie_info['original_title'].strip():
                        title_sources.append(('movie_info.original_title', movie_info['original_title']))
                    
                    # 来源3: movie_info中的originaltitle（豆瓣常用字段）
                    if movie_info.get('originaltitle') and movie_info['originaltitle'] and movie_info['originaltitle'].strip():
                        title_sources.append(('movie_info.originaltitle', movie_info['originaltitle']))
                    
                    # 来源4: detail_info中的title
                    if movie_info.get('detail_info') and movie_info['detail_info'].get('title') and movie_info['detail_info']['title'].strip():
                        title_sources.append(('detail_info.title', movie_info['detail_info']['title']))
                    
                    # 来源5: 从nfo_content中提取
                    if movie_info.get('nfo_content'):
                        import re
                        title_match = re.search(r'<title>(.*?)</title>', movie_info['nfo_content'])
                        if title_match:
                            extracted_title = title_match.group(1).strip()
                            if extracted_title and extracted_title.strip() and extracted_title.strip() not in ['未知视频', '未知', '', '电影详情']:
                                title_sources.append(('nfo_content', extracted_title))
                    
                    # 记录所有找到的标题来源
                    logger.info(f"找到的标题来源: {title_sources}")
                    
                    # 选择最佳的标题来源
                    if title_sources:
                        # 优先选择detail_info中的title，然后是movie_info.title等
                        best_source, best_title = title_sources[0]  # 默认选择第一个来源
                        
                        # 寻找最合适的标题（不是默认值）
                        for source, title in title_sources:
                            if title and title not in ['未知视频', '未知', '', '电影详情']:
                                best_source = source
                                best_title = title
                                break
                        
                        movie_info['title'] = best_title
                        logger.info(f"选择的标题: '{best_title}' (来源: {best_source})")
                    else:
                        logger.warning("未找到有效的标题来源")
                    
                    # 确保标题不是无效值
                    if not movie_info.get('title') or movie_info['title'] in ['未知视频', '未知', '', '电影详情']:
                        # 使用media_name作为最后的备选
                        if media_name and media_name not in ['未知视频', '未知', '', '电影详情']:
                            movie_info['title'] = media_name
                            logger.info(f"使用media_name作为标题: {media_name}")
                        else:
                            # 如果所有来源都失败，使用一个合理的默认值
                            movie_info['title'] = '未知电影'
                            logger.warning("无法找到有效的标题，使用默认值")
                    
                    # 记录最终的标题
                    logger.info(f"最终确定的标题: {movie_info['title']}")
                    logger.info(f"处理后的movie_info标题: {movie_info.get('title')}")
                except Exception as e:
                    logger.error(f"处理豆瓣标题时出错: {str(e)}", exc_info=True)
        
        # 使用本地资源管理工具保存刮削数据
        success, result = local_resources_utils.save_scraped_data(resource_path, movie_info, poster_path)
        
        if success:
            return jsonify({
                'success': True,
                'message': '刮削数据已保存成功',
                'data': result['data']
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': result
            }), 500
        
    except Exception as e:
        logger.error(f'保存刮削数据时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 生成NFO文件
@app.route('/api/douban/generate-nfo', methods=['POST'])
def generate_nfo():
    """生成NFO文件API"""
    try:
        data = request.json
        url = data.get('url', '')
        
        if not url:
            return jsonify({
                'success': False,
                'message': '请提供电影详情URL'
            }), 400
            
        # 从URL中提取电影名称作为keyword参数
        movie_utils = get_douban_movie_utils("电影详情")  # 使用通用名称初始化
        detail = movie_utils.get_detail_info(url)
        
        if not detail:
            return jsonify({
                'success': False,
                'message': '无法获取电影详情'
            }), 400
            
        # 生成NFO内容
        nfo_content = movie_utils.generate_nfo(detail)
        if not nfo_content:
            return jsonify({
                'success': False,
                'message': '生成NFO内容失败'
            }), 500
            
        # 获取配置中的NFO保存目录
        config = load_config()
        nfo_dir = config['app'].get('nfo_dir', './data/nfo_files')
        os.makedirs(nfo_dir, exist_ok=True)
        
        # 生成安全的文件名
        safe_title = re.sub(r'[\/:*?"<>|]', '', detail.get('title', 'movie'))
        nfo_filename = f"{safe_title}.nfo"
        nfo_path = os.path.join(nfo_dir, nfo_filename)
        
        # 保存NFO文件
        with open(nfo_path, 'w', encoding='utf-8') as f:
            f.write(nfo_content)
            
        # 下载海报图片
        poster_path = None
        if detail.get('poster_url'):
            poster_path = movie_utils.download_poster(
                detail['poster_url'],
                detail['title'],
                detail['douban_id']
            )
        
        return jsonify({
            'success': True,
            'data': {
                'nfo_path': nfo_path,
                'poster_path': poster_path,
                'movie_info': detail
            }
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# 系统配置页面路由
@app.route('/config')
@permission_required('admin')
def config_page():
    """系统配置页面"""
    config = load_config()
    return render_template('config.html', config=config)

# 本地资源页面路由
@app.route('/local-resources')
@permission_required('admin')
def local_resources_page():
    """本地资源页面"""
    return render_template('local_resources.html')

# qBittorrent页面路由
@app.route('/qbittorrent')
@permission_required('admin')
def qbittorrent_page():
    """qBittorrent下载器页面"""
    return render_template('qbittorrent.html')

# Transmission页面路由
@app.route('/transmission')
@permission_required('admin')
def transmission_page():
    """Transmission下载器页面"""
    return render_template('transmission.html')

# PTSkit种子页面路由
@app.route('/ptskit')
@permission_required('admin')
def ptskit_page():
    """PTSkit种子搜索页面"""
    return render_template('ptskit.html')

# 获取PTSkit单例实例
_ptskit_spider = None

def get_ptskit_spider():
    """获取PTSkitSpider单例实例，并关联QBittorrent和Transmission管理器"""
    global _ptskit_spider
    if _ptskit_spider is None:
        config = load_config()
        cookie = config.get('ptskit', {}).get('cookie', '')
        qb_manager = get_qbittorrent_manager()
        tr_manager = get_transmission_manager()
        _ptskit_spider = PTSkitSpider(
            cookie=cookie,
            qb_manager=qb_manager,
            tr_manager=tr_manager
        )
    return _ptskit_spider

# API路由 - 获取种子列表
@app.route('/api/ptskit/torrents', methods=['GET'])
def get_torrents():
    """获取PTSkit种子列表API"""
    try:
        # 获取查询参数
        page = int(request.args.get('page', 0))
        filter_short_drama = request.args.get('filter_short_drama', 'true').lower() == 'true'
        search_query = request.args.get('q', '')
        
        # 获取PTSkit实例
        ptskit_spider = get_ptskit_spider()
        
        # 获取种子列表
        torrents = ptskit_spider.get_torrents(page=page, filter_short_drama=filter_short_drama)
        
        # 如果有搜索查询，进行过滤
        if search_query:
            search_query = search_query.lower()
            torrents = [t for t in torrents if 
                        search_query in t['title'].lower() or 
                        search_query in t['tags'].lower() or 
                        search_query in t['uploader'].lower()]
        
        return jsonify({
            'success': True,
            'data': torrents
        }), 200
        
    except Exception as e:
        logger.error(f'获取种子列表时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 获取单个种子信息
@app.route('/api/ptskit/torrent', methods=['GET'])
@permission_required('admin')
def get_torrent_detail():
    """获取单个种子详细信息API"""
    try:
        # 获取查询参数
        torrent_id = request.args.get('id', '')
        
        if not torrent_id:
            return jsonify({
                'success': False,
                'message': '请提供种子ID'
            }), 400
        
        # 获取PTSkit实例
        ptskit_spider = get_ptskit_spider()
        
        # 获取第一页的种子列表，然后找到匹配ID的种子
        # 注意：这是一个简化的实现，实际应用中可能需要优化
        page = 0
        found_torrent = None
        
        # 尝试在最多10页内查找
        while page < 10 and not found_torrent:
            torrents = ptskit_spider.get_torrents(page=page, filter_short_drama=False)
            found_torrent = next((t for t in torrents if t['id'] == torrent_id), None)
            page += 1
        
        if found_torrent:
            return jsonify({
                'success': True,
                'data': found_torrent
            }), 200
        else:
            return jsonify({
                'success': False,
                'message': f'未找到ID为{torrent_id}的种子'
            }), 404
        
    except Exception as e:
        logger.error(f'获取种子详情时发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# 获取系统配置API
@app.route('/api/config/get', methods=['GET'])
def get_config():
    """获取系统配置API"""
    try:
        config = load_config()
        return jsonify({
            'success': True,
            'data': config
        }), 200
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# 获取资源统计数据API
@app.route('/api/resources/stats', methods=['GET'])
def get_resources_stats():
    """获取资源统计数据"""
    try:
        stats = {
            'local_resources': 0,
            'fnos_media_libraries': 0,
            'emby_media_libraries': 0,
            'qbittorrent_tasks': 0,
            'transmission_tasks': 0
        }
        
        # 获取本地资源数量
        try:
            resource_stats = local_resources_utils.get_resource_stats()
            # 返回视频文件和文件夹的总数
            stats['local_resources'] = resource_stats.get('total', 0)
        except Exception as e:
            app.logger.warning(f"获取本地资源数量失败: {str(e)}")
        
        # 获取飞牛媒体库数量
        try:
            from utils.fnos_media_utils import FnosMediaFetcher
            fnos_config = config.get('fnos_media', {})
            fetcher = FnosMediaFetcher(
                server_ip=fnos_config.get('server_ip', 'localhost'),
                server_port=fnos_config.get('server_port', 8088),
                verify_ssl=fnos_config.get('verify_ssl', False)
            )
            
            success, message = fetcher.login(
                fnos_config.get('username', 'admin'),
                fnos_config.get('password', 'admin')
            )
            
            if success:
                success, data = fetcher.fetch_mediadb_list()
                if success:
                    stats['fnos_media_libraries'] = len(data)
        except Exception as e:
            app.logger.warning(f"获取飞牛媒体库数量失败: {str(e)}")
        
        # 获取Emby媒体库数量
        try:
            from utils.emby_media_utils import EmbyMediaUtils
            emby_config = config.get('emby', {})
            emby_utils = EmbyMediaUtils(
                server_url=emby_config.get('server_url', 'localhost'),
                server_port=emby_config.get('server_port', 8096),
                api_key=emby_config.get('api_key', ''),
                user_id=emby_config.get('user_id', '')
            )
            
            libraries = emby_utils.get_media_libraries()
            if libraries:
                stats['emby_media_libraries'] = len(libraries.get('libraries', []))
        except Exception as e:
            app.logger.warning(f"获取Emby媒体库数量失败: {str(e)}")
        
        # 获取qBittorrent任务总数
        try:
            from utils.qbittorrent_utils import QBittorrentManager
            qb_manager = get_qbittorrent_manager()
            if qb_manager and qb_manager.connect():
                tasks = qb_manager.get_downloads()
                stats['qbittorrent_tasks'] = len(tasks)
        except Exception as e:
            app.logger.warning(f"获取qBittorrent任务数失败: {str(e)}")
        
        # 获取Transmission任务总数
        try:
            from utils.transmission_utils import TransmissionManager
            tr_manager = get_transmission_manager()
            if tr_manager and tr_manager.connect():
                tasks = tr_manager.get_downloads()
                stats['transmission_tasks'] = len(tasks)
        except Exception as e:
            app.logger.warning(f"获取Transmission任务数失败: {str(e)}")
        
        return jsonify({
            'success': True,
            'data': stats
        }), 200
    except Exception as e:
        app.logger.error(f"获取资源统计数据失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# API路由 - 测试qBittorrent连接
@app.route('/api/qbittorrent/test-connection', methods=['POST'])
@permission_required('admin')
def test_qbittorrent_connection():
    """测试qBittorrent连接API"""
    try:
        # 获取配置中的qBittorrent设置
        config = load_config()
        qb_config = config.get('qbittorrent', {})
        
        # 检查必要配置项是否为空字符串
        host = qb_config.get('host', '')
        port = qb_config.get('port', 8080)
        username = qb_config.get('username', '')
        
        if not host or host == '':
            return jsonify({
                'success': False,
                'message': 'qBittorrent主机地址未配置'
            }), 400
        
        # 创建临时qBittorrent管理器进行连接测试
        from utils.qbittorrent_utils import QBittorrentManager
        qb_manager = QBittorrentManager({
            'host': host,
            'port': port,
            'username': username,
            'password': qb_config.get('password', '')
        })
        
        # 尝试连接 - 使用connect()进行实际的API连接和认证测试
        is_connected = qb_manager.connect()
        
        return jsonify({
            'success': True,
            'data': {
                'is_connected': is_connected,
                'host': host,
                'error': qb_manager.last_error if not is_connected else None
            }
        }), 200
        
    except Exception as e:
        app.logger.error(f"测试qBittorrent连接失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'测试qBittorrent连接失败: {str(e)}'
        }), 500

# 获取qBittorrent任务列表
@app.route('/api/qbittorrent/tasks', methods=['GET'])
def get_qbittorrent_tasks():
    """获取qBittorrent任务列表"""
    try:
        qb_manager = get_qbittorrent_manager()
        tasks = qb_manager.get_downloads()
        return jsonify({'success': True, 'data': tasks})
    except Exception as e:
        app.logger.error(f"获取qBittorrent任务列表失败: {str(e)}")
        return jsonify({'success': False, 'message': f'获取qBittorrent任务列表失败: {str(e)}'}), 500

# 添加qBittorrent任务
@app.route('/api/qbittorrent/add-task', methods=['POST'])
def add_qbittorrent_task():
    """通过PTSkitSpider添加qBittorrent任务，确保使用爬虫的session下载种子文件"""
    try:
        data = request.json
        torrent_url = data.get('torrent_url')
        save_path = data.get('save_path', None)
        start_paused = data.get('start_paused', False)
        
        if not torrent_url:
            return jsonify({'success': False, 'message': '请提供种子URL'}), 400
            
        # 使用PTSkitSpider来添加任务，利用其session保持认证状态
        ptskit_spider = get_ptskit_spider()
        
        # 导入UUID模块生成唯一ID
        import uuid
        
        # 创建一个完整的torrent_info对象，包含所有必要字段
        torrent_info = {
            'id': f'api_call_{uuid.uuid4().hex[:8]}',  # 为每个API调用生成唯一ID
            'download_url': torrent_url,
            'category': 'API添加',  # 添加默认分类
            'tags': '无'  # 添加默认标签
        }
        
        # 调用PTSkitSpider的add_torrent_to_qb方法
        success = ptskit_spider.add_torrent_to_qb(torrent_info, save_path=save_path, start_paused=start_paused)
        
        if success:
            return jsonify({'success': True, 'message': '任务添加成功'})
        else:
            return jsonify({'success': False, 'message': '添加任务失败，请检查日志'}), 500
    except Exception as e:
        app.logger.error(f"添加qBittorrent任务失败: {str(e)}")
        return jsonify({'success': False, 'message': f'添加qBittorrent任务失败: {str(e)}'}), 500

# API路由 - 测试Transmission连接
@app.route('/api/transmission/test-connection', methods=['POST'])
@permission_required('admin')
def test_transmission_connection():
    """测试Transmission连接API"""
    try:
        # 获取配置中的Transmission设置
        config = load_config()
        tr_config = config.get('transmission', {})
        
        # 检查必要配置项是否为空字符串
        host = tr_config.get('host', '')
        port = tr_config.get('port', 9091)
        
        if not host or host == '':
            return jsonify({
                'success': False,
                'message': 'Transmission主机地址未配置'
            }), 400
        
        # 创建临时Transmission管理器进行连接测试
        from utils.transmission_utils import TransmissionManager
        tr_manager = TransmissionManager({
            'host': host,
            'port': port,
            'username': tr_config.get('username', ''),
            'password': tr_config.get('password', '')
        })
        
        # 尝试连接 - 使用connect()进行实际的API连接和认证测试
        is_connected = tr_manager.connect()
        
        return jsonify({
            'success': True,
            'data': {
                'is_connected': is_connected,
                'host': host,
                'error': tr_manager.last_error if not is_connected else None
            }
        }), 200
        
    except Exception as e:
        app.logger.error(f"测试Transmission连接失败: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'测试Transmission连接失败: {str(e)}'
        }), 500

# 获取Transmission任务列表
@app.route('/api/transmission/tasks', methods=['GET'])
def get_transmission_tasks():
    """获取Transmission任务列表"""
    try:
        tr_manager = get_transmission_manager()
        tasks = tr_manager.get_downloads()
        return jsonify({'success': True, 'data': tasks})
    except Exception as e:
        app.logger.error(f"获取Transmission任务列表失败: {str(e)}")
        return jsonify({'success': False, 'message': f'获取Transmission任务列表失败: {str(e)}'}), 500

# 添加Transmission任务
@app.route('/api/transmission/add-task', methods=['POST'])
@permission_required('admin')
def add_transmission_task():
    """添加任务到Transmission下载器，通过PTSkitSpider确保认证检查"""
    try:
        data = request.json
        torrent_url = data.get('torrent_url')
        save_path = data.get('save_path', None)
        start_paused = data.get('start_paused', False)
        
        if not torrent_url:
            return jsonify({'success': False, 'message': '请提供种子URL'}), 400
            
        # 使用PTSkitSpider处理种子添加，确保认证状态
        ptskit_spider = get_ptskit_spider()
        
        # 导入UUID模块生成唯一ID
        import uuid
        
        # 创建一个完整的torrent_info对象，包含所有必要字段
        torrent_info = {
            'id': f'tr_api_call_{uuid.uuid4().hex[:8]}',  # 为每个API调用生成唯一ID
            'download_url': torrent_url,
            'category': 'API添加',  # 添加默认分类
            'tags': '无'  # 添加默认标签
        }
        
        # 通过PTSkitSpider添加到Transmission
        success = ptskit_spider.add_torrent_to_transmission(
            torrent_info=torrent_info,
            save_path=save_path,
            start_paused=start_paused
        )
        
        if success:
            return jsonify({'success': True, 'message': '任务已成功添加到Transmission'})
        else:
            return jsonify({'success': False, 'message': '添加Transmission任务失败'}), 500
    except Exception as e:
        app.logger.error(f"添加Transmission任务失败: {str(e)}")
        return jsonify({'success': False, 'message': f'添加Transmission任务失败: {str(e)}'}), 500

# 更新系统配置
@app.route('/api/config/update', methods=['POST'])
def update_config():
    """更新系统配置"""
    try:
        config = request.json
        config_path = os.path.join(os.path.dirname(__file__), 'config', 'config.json')
        with open(config_path, 'w', encoding='utf-8') as f:
            json.dump(config, f, ensure_ascii=False, indent=2)
        # 重置单例实例，使新配置生效
        global _ptskit_spider, _qbittorrent_manager, _transmission_manager, _fnos_media_fetcher, _emby_media_utils
        _ptskit_spider = None
        _qbittorrent_manager = None
        _transmission_manager = None
        _fnos_media_fetcher = None
        _emby_media_utils = None
        return jsonify({'success': True, 'message': '配置更新成功'})
    except Exception as e:
        app.logger.error(f"更新配置失败: {str(e)}")
        return jsonify({'success': False, 'message': f'配置更新失败: {str(e)}'}), 500

# Emby媒体服务器路由
@app.route('/emby_media')
@permission_required('admin')
def emby_media():
    """Emby媒体服务器页面"""
    return render_template('emby_media.html')

# 飞牛媒体库路由
@app.route('/fnos_media')
@permission_required('admin')
def fnos_media():
    """飞牛媒体库页面"""
    return render_template('fnos_media.html')

# API路由 - 测试飞牛媒体库连接
@app.route('/api/fnos_media/test-connection', methods=['GET'])
@permission_required('admin')
def test_fnos_media_connection():
    """测试飞牛媒体库连接"""
    try:
        config = load_config()
        fnos_config = config.get('fnos_media', {})
        
        # 创建临时实例进行连接测试
        fetcher = FnosMediaFetcher(
            server_ip=fnos_config.get('server_ip', 'localhost'),
            server_port=fnos_config.get('server_port', 8088),
            verify_ssl=fnos_config.get('verify_ssl', False)
        )
        
        # 尝试登录
        success, message = fetcher.login(
            fnos_config.get('username', 'admin'),
            fnos_config.get('password', 'admin')
        )
        
        if success:
            return jsonify({'success': True, 'message': message})
        else:
            return jsonify({'success': False, 'message': message}), 400
    except Exception as e:
        app.logger.error(f"测试飞牛媒体库连接失败: {str(e)}")
        return jsonify({'success': False, 'message': f'连接失败: {str(e)}'}), 500

# API路由 - 获取飞牛媒体库列表
@app.route('/api/fnos_media/library-list', methods=['GET'])
def get_fnos_media_library_list():
    """获取飞牛媒体库列表"""
    try:
        config = load_config()
        fnos_config = config.get('fnos_media', {})
        
        app.logger.info(f"尝试连接飞牛媒体库: {fnos_config.get('server_ip', 'localhost')}:{fnos_config.get('server_port', 8088)}")
        
        # 创建实例
        fetcher = FnosMediaFetcher(
            server_ip=fnos_config.get('server_ip', 'localhost'),
            server_port=fnos_config.get('server_port', 8088),
            verify_ssl=fnos_config.get('verify_ssl', False)
        )
        
        # 登录
        success, message = fetcher.login(
            fnos_config.get('username', 'admin'),
            fnos_config.get('password', 'admin')
        )
        
        if not success:
            app.logger.error(f"飞牛媒体库登录失败: {message}")
            return jsonify({'success': False, 'message': message}), 400
        
        # 获取媒体库列表
        success, data = fetcher.fetch_mediadb_list()
        
        if success:
            app.logger.info(f"成功获取飞牛媒体库列表，共 {len(data)} 个媒体库")
            # 确保返回的数据格式与前端期望的一致
            return jsonify({'success': True, 'data': data})
        else:
            app.logger.error(f"获取飞牛媒体库列表失败: {data}")
            return jsonify({'success': False, 'message': data}), 400
    except Exception as e:
        app.logger.error(f"获取飞牛媒体库列表异常: {str(e)}")
        return jsonify({'success': False, 'message': f'获取失败: {str(e)}'}), 500

# API路由 - 获取媒体条目列表
@app.route('/api/fnos_media/media-list', methods=['GET'])
def get_fnos_media_list():
    """获取媒体条目列表"""
    try:
        library_guid = request.args.get('library_guid')
        
        if not library_guid:
            return jsonify({'success': False, 'message': '请提供媒体库ID'}), 400
        
        config = load_config()
        fnos_config = config.get('fnos_media', {})
        
        # 创建实例
        fetcher = FnosMediaFetcher(
            server_ip=fnos_config.get('server_ip', 'localhost'),
            server_port=fnos_config.get('server_port', 8088),
            verify_ssl=fnos_config.get('verify_ssl', False)
        )
        
        # 登录
        success, message = fetcher.login(
            fnos_config.get('username', 'admin'),
            fnos_config.get('password', 'admin')
        )
        
        if not success:
            return jsonify({'success': False, 'message': message}), 400
        
        # 获取媒体条目
        success, media_items = fetcher.fetch_media(library_guid)
        
        if not success:
            return jsonify({'success': False, 'message': media_items}), 400
        
        # 提取媒体信息
        success, processed_info = fetcher.save_media_info(media_items)
        
        if success:
            return jsonify({'success': True, 'data': processed_info})
        else:
            return jsonify({'success': False, 'message': processed_info}), 400
    except Exception as e:
        app.logger.error(f"获取媒体条目列表失败: {str(e)}")
        return jsonify({'success': False, 'message': f'获取失败: {str(e)}'}), 500

# API路由 - 清理临时文件
@app.route('/api/fnos_media/cleanup-temp', methods=['POST'])
def cleanup_temp_files():
    """清理临时生成的媒体库封面文件"""
    try:
        data = request.json
        files_to_clean = data.get('files', [])
        
        # 临时存储目录
        temp_dir = os.path.join(app.root_path, 'static', 'temp')
        
        if not os.path.exists(temp_dir):
            return jsonify({'success': True, 'message': '临时目录不存在'})
        
        cleaned_count = 0
        
        for file_url in files_to_clean:
            try:
                # 从URL中提取文件名
                file_name = os.path.basename(file_url)
                file_path = os.path.join(temp_dir, file_name)
                
                # 确保文件在临时目录内，防止路径遍历攻击
                if os.path.exists(file_path) and os.path.commonpath([temp_dir, file_path]) == temp_dir:
                    os.remove(file_path)
                    cleaned_count += 1
                    app.logger.debug(f"已清理临时文件: {file_path}")
            except Exception as e:
                app.logger.warning(f"清理临时文件失败: {str(e)}")
                # 继续清理其他文件
        
        app.logger.info(f"成功清理了 {cleaned_count} 个临时文件")
        return jsonify({'success': True, 'cleaned_count': cleaned_count})
    except Exception as e:
        app.logger.error(f"清理临时文件异常: {str(e)}")
        return jsonify({'success': False, 'message': f'清理失败: {str(e)}'}), 500

# API路由 - 获取媒体库拼接封面
@app.route('/api/fnos_media/library-poster', methods=['GET'])
def get_library_stitched_poster():
    """获取媒体库拼接后的封面图片"""
    try:
        library_guid = request.args.get('library_guid')
        
        if not library_guid:
            return jsonify({'success': False, 'message': '请提供媒体库ID'}), 400
        
        config = load_config()
        fnos_config = config.get('fnos_media', {})
        
        # 创建实例
        fetcher = FnosMediaFetcher(
            server_ip=fnos_config.get('server_ip', 'localhost'),
            server_port=fnos_config.get('server_port', 8088),
            verify_ssl=fnos_config.get('verify_ssl', False)
        )
        
        # 登录
        success, message = fetcher.login(
            fnos_config.get('username', 'admin'),
            fnos_config.get('password', 'admin')
        )
        
        if not success:
            app.logger.error(f"飞牛媒体库登录失败: {message}")
            return jsonify({'success': False, 'message': message}), 400
        
        # 获取媒体库列表以获取媒体库信息
        success, libraries = fetcher.fetch_mediadb_list()
        if not success:
            app.logger.error(f"获取媒体库列表失败: {libraries}")
            return jsonify({'success': False, 'message': libraries}), 400
        
        # 查找指定的媒体库
        library = next((lib for lib in libraries if lib["guid"] == library_guid), None)
        if not library:
            return jsonify({'success': False, 'message': f'未找到GUID为 {library_guid} 的媒体库'}), 404
        
        # 临时存储目录
        temp_dir = os.path.join(app.root_path, 'static', 'temp')
        os.makedirs(temp_dir, exist_ok=True)
        
        # 生成临时文件路径
        safe_title = re.sub(r'[\\/:*?"<>|]', '_', library['title'])
        temp_file = os.path.join(temp_dir, f'{safe_title}_{library_guid}_poster.png')
        
        # 调用拼接函数
        success, message = fetcher.stitch_library_posters(library_guid, temp_file)
        
        if success:
            # 生成可访问的URL路径
            poster_url = f'/static/temp/{safe_title}_{library_guid}_poster.png'
            app.logger.info(f"成功生成媒体库拼接封面: {poster_url}")
            return jsonify({'success': True, 'poster_url': poster_url})
        else:
            app.logger.error(f"生成媒体库拼接封面失败: {message}")
            # 如果生成失败，返回第一张海报或占位图
            fallback_poster = library.get('posters', [])[0] if library.get('posters', []) else '/static/images/placeholder.png'
            return jsonify({'success': True, 'poster_url': fallback_poster})
    except Exception as e:
        app.logger.error(f"获取媒体库拼接封面异常: {str(e)}")
        return jsonify({'success': False, 'message': f'获取失败: {str(e)}'}), 500

# API路由 - 获取媒体详情
@app.route('/api/fnos_media/media-detail', methods=['GET'])
def get_fnos_media_detail():
    """获取媒体详情"""
    try:
        media_guid = request.args.get('media_guid')
        library_guid = request.args.get('library_guid')
        
        if not media_guid or not library_guid:
            return jsonify({'success': False, 'message': '请提供媒体ID和媒体库ID'}), 400
        
        config = load_config()
        fnos_config = config.get('fnos_media', {})
        
        # 创建实例
        fetcher = FnosMediaFetcher(
            server_ip=fnos_config.get('server_ip', 'localhost'),
            server_port=fnos_config.get('server_port', 8088),
            verify_ssl=fnos_config.get('verify_ssl', False)
        )
        
        # 登录
        success, message = fetcher.login(
            fnos_config.get('username', 'admin'),
            fnos_config.get('password', 'admin')
        )
        
        if not success:
            return jsonify({'success': False, 'message': message}), 400
        
        # 获取媒体条目
        success, media_items = fetcher.fetch_media(library_guid)
        
        if not success:
            return jsonify({'success': False, 'message': media_items}), 400
        
        # 查找指定的媒体条目
        target_item = None
        for item in media_items:
            if item.get('guid') == media_guid:
                target_item = item
                break
        
        if not target_item:
            return jsonify({'success': False, 'message': '未找到指定的媒体条目'}), 404
        
        # 提取媒体信息
        success, processed_info = fetcher.save_media_info([target_item])
        
        if success and processed_info and len(processed_info) > 0:
            return jsonify({'success': True, 'data': processed_info[0]})
        else:
            return jsonify({'success': False, 'message': processed_info or '处理媒体信息失败'}), 400
    except Exception as e:
        app.logger.error(f"获取媒体详情失败: {str(e)}")
        return jsonify({'success': False, 'message': f'获取失败: {str(e)}'}), 500

# API路由 - 下载NFO文件
@app.route('/api/douban/download-nfo', methods=['GET'])
def download_nfo():
    """下载NFO文件API"""
    try:
        file_path = request.args.get('path', '')
        if not file_path:
            return jsonify({
                'success': False,
                'message': '请提供NFO文件路径'
            }), 400
        
        # 确保文件存在
        if not os.path.exists(file_path):
            return jsonify({
                'success': False,
                'message': 'NFO文件不存在'
            }), 404
        
        # 获取文件名
        filename = os.path.basename(file_path)
        
        # 返回文件下载响应
        with open(file_path, 'rb') as f:
            response = app.response_class(
                response=f.read(),
                status=200,
                mimetype='application/octet-stream'
            )
            response.headers["Content-Disposition"] = f"attachment; filename={filename}"
            return response
            
    except Exception as e:
        return jsonify({
            'success': False,
            'message': str(e)
        }), 500

# 错误处理
@app.errorhandler(404)
def page_not_found(e):
    """处理404错误"""
    return jsonify({
        'success': False,
        'message': '请求的资源不存在'
    }), 404

@app.errorhandler(500)
def internal_server_error(e):
    """处理500错误"""
    return jsonify({
        'success': False,
        'message': '服务器内部错误'
    }), 500

# 应用启动入口
if __name__ == '__main__':
    # 加载配置
    config = load_config()
    # 设置日志级别
    log_level = config.get('app', {}).get('log_level', 'INFO')
    logging.basicConfig(level=getattr(logging, log_level), format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    # 获取端口
    port = config.get('app', {}).get('port', 5000)
    # 启动Flask应用
    app.run(host='0.0.0.0', port=port, debug=config.get('app', {}).get('debug', False))